/******************************************************************************
Class:Renderer		
Implements:OGLRenderer
Author:Rich Davison	<richard.davison4@newcastle.ac.uk> and YOU!
Description: For this module, you are being provided with a basic working
Renderer - to give you more time to work on your physics and AI!

It is basically the Renderer from the Graphics For Games Module as it was
by Tutorial 7 - Scene Management. It will split nodes up into those that are
opaque and transparent, and render accordingly. 

The only new bits are the ability to add and remove scene nodes, and set the
camera - as these are now to be controlled by your 'game'.

The renderer is one of those things that really there should be only one of.
I think it was a mistake to add support for multiple Windows / Renderers to
the code download, as it just overcomplicated things. To this end, The 
Renderer for this module is a singleton-like class, as with the 
PhysicsSystem. Thus, it is created, accessed, and destroyed via static 
functions. You don't have to do it this way if you don't want, and you are
of course free to add in the advanced graphical features from other tutorials
into this Renderer as you see fit. 


-_-_-_-_-_-_-_,------,   
_-_-_-_-_-_-_-|   /\_/\   NYANYANYAN
-_-_-_-_-_-_-~|__( ^ .^) /
_-_-_-_-_-_-_-""  ""   

*//////////////////////////////////////////////////////////////////////////////


#pragma once

#include "../../nclgl/OGLRenderer.h"
#include "../../nclgl/SceneNode.h"
#include "../../nclgl/Camera.h"
#include "../../nclgl/Frustum.h"
#include "../../nclgl/Light.h"
#include "../../nclgl/OBJMesh.h"
#include <algorithm>
#include "TerrainClass.h"
#include "TextMesh.h"
#include "Button.h"
#include "WavesMesh.h"
#include "ParticleEmitter.h"

/*	this enumeration stores all the index for the shaders
*	make sure when you initializing the shaders you need, you add the index here and put the name before max shader count.
*/
enum ShaderIndex { SIMPLESHADER, PERPIXELSHADER, BUMPSHADER, SHADOWSHADER, SHADOWSCENESHADER, POSTSHADER, REFLECTSHADER, PARTICLESHADER, MAXSHADERCOUNT };
enum FboIndex	 {MAINSCENE,WATERSCENE,REFLECTSCENE,MAXSCENECOUNT};
enum LensflareFBOIndex { HDRBUFFER, AUXABUFFER, AUXBBUFFER, AUXCBUFFER, BLOOMBUFFER, MAXLENSFLAREBUFFER};
enum LensflareTextureIndex { HDRTEXTURE, HDRDEPTHTEXTURE, AUXATEXTURE, AUXBTEXTURE, AUXCTEXTURE, MAXLENSFLARETEXTURE };

#define SHADOWSIZE 8192 //the size of the detail of shadows
#define SHADOWZNEAR 1000.0f //the near plane of shadow
#define WORLD_LIGHT_STARTING_POSITION Vector3(0.0f,1500.0f,-3000.0f)
#define WORLD_LIGHT_DIRECTION Vector3(0,-500.0f,500.0f)
#define WORLD_LIGHT_VIEWANGLE 90.0f

#define zNear 200.0f
#define zFar  30000.0f


#define FOG_TIME  3000.0f
class Renderer : public OGLRenderer	{
public:
	virtual void RenderScene();
	virtual void UpdateScene(float msec);

	void	SetCamera(Camera*c);
	Camera* GetCamera(){ return camera;}

	void	AddNode(SceneNode* n);

	void	RemoveNode(SceneNode* n);

	//Statics
	static bool Initialise() {
		instance = new Renderer(Window::GetWindow());
		return instance->HasInitialised();
	}


	void	DrawLoadingScreen(GLuint texture);

	static void Destroy() {
		delete instance;
	}
	
	static Renderer&GetRenderer() { return *instance;}
	void    SetIsblur(bool value){ isBlurred = value; }//added by Matrix to blur the scene when player rush
	void    SetPlayerVelocity(float velocity){ playerVelocity = -velocity; }
	void	SetScreenSize(Vector2 scr){ screenSize = scr; }

	void   SetIsFog(bool value){ isFog = value; }
	bool   GetIsFog(){ return isFog; }
	//////////PARTICLE STUFF////////
	void StartExplosionParticle();
	void StopExplosionParticle();
	void SetExplosionPosition(const Vector3& pos){ ExplosionPosition = pos; };

	bool isMultiPlayer;
protected:	
	Renderer(Window &parent);
	virtual ~Renderer(void);

	void	BuildNodeLists(SceneNode* from);
	void	SortNodeLists();
	void	ClearNodeLists();
	void	DrawNodes();
	void	DrawWorldLight();
	void	DrawNode(SceneNode*n);
	void	DrawCombinedScene();
	void	DrawShadowScene();
	bool	InitializeShaders();
	bool	InitializeWorldLight();
	void    DrawWater();
	float   time;
	void	InitSkyDome();
	void	DrawSkyDome(float time);
	void	DrawParticle();

	Matrix4 shadowMatrix;

	GLuint currentFBO;

	//----shadow related----
	bool GenerateShadowBuffer();
	//the shadow buffer texture.
	GLuint shadowTex;
	//the shadow frame object.
	GLuint shadowFBO;
	//----shadow related ends----

	//----lensflare related----
	GLuint lensflareBuffer[MAXLENSFLAREBUFFER];
	GLuint lenflareTexture[MAXLENSFLARETEXTURE];
	float bloomScale;
    float bloomBlurRadius;
	float flareScale;
	float flareBlurRadius;
	void InitializeLensFlareBuffer();
	//----lensflare related ends----

	Light* worldLight;
	SceneNode* sun;
	Mesh* sphere;
	Mesh* quad;
	WavesMesh* wavesQuad;

	SceneNode*	root;
	Camera*		camera;
	vector<Shader*> shaders;

	Frustum		frameFrustum;

	vector<SceneNode*> transparentNodeList;
	vector<SceneNode*> nodeList;

	static Renderer*	instance;

	float angle;

	//////////HUD STUFF//////////

	//draws HUD Elements
	void DrawHudNodes(SceneNode* hudroot);
	void DrawHUD();
	void DrawText(const std::string &text, const Vector3 &position, const float size = 10.0f, const bool perspective = false);

	//root for all Hud elements
	SceneNode* HUD_root;
	Font*	basicFont;	//A font! a basic one...

	void	AddHUDNode(SceneNode* n);

	void	RemoveHUDNode(SceneNode* n);

	//////////////add by Aaron, water , reflect , post /////////////
	void    InitBuffers();
	void    DrawPostProcess();
	void	GenerateScreenTexture(GLuint & into, bool depth = false);
	GLuint	bufferFBO[3];
	GLuint  bufferColourTex[3];
	GLuint  bufferDepthTex[3];
	GLuint  offSetTex;

	bool    isInversed;
	bool    isBlurred;
	float   playerVelocity;
	Vector2 screenSize;
	//Fog STUFF//////
	void  DrawFog();

	GLfloat Density;
	Vector4 fogColor;
	bool    isFog;
	float   fogTimer;

	//////////PARTICLE STUFF////////
	void	SetShaderParticleSize(float f);
	int		weatherTime;
	int     rainTime;
	int     snowTime;
	bool    dropRain;
	bool    dropSnow;
	bool	doExplosion;
	ParticleEmitter*	Rain;
	ParticleEmitter*	Snow;
	ParticleEmitter*	Explosion;
	Vector3				ExplosionPosition;

	Mesh* Skydome;

	//loading screen textures
	GLuint loadingTex1;
	GLuint loadingTex2;
	GLuint loadingTex3;

};

